<template>
  <div class="tags-view-container">
    <scroll-panel>
      <div class="tags-view-wrapper">
        <router-link class="tags-view-item"
                     :class="{active: isActive(tag)}"
                     :style="{
                      backgroundColor: isActive(tag) ? themeColor : '',
                      borderColor: isActive(tag) ? themeColor : '' }"
                     v-for="(tag, index) in visitedTags"
                     :key="index"
                     :to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
                     tag="span">
          <el-dropdown trigger="contextmenu"
                       @command="command => handleTagCommand(command, tag)">
            <span>
              {{ tag.meta.title }}
              <!-- affix固定的路由tag是无法删除 -->
              <span v-if="!isAffix(tag)"
                    class="el-icon-close"
                    @click.prevent.stop="closeSelectedTag(tag)"></span>
            </span>
            <template #dropdown>
              <el-dropdown-menu>
                <el-dropdown-item command="refresh">刷新</el-dropdown-item>
                <el-dropdown-item command="all">关闭所有</el-dropdown-item>
                <el-dropdown-item command="other">关闭其他</el-dropdown-item>
                <el-dropdown-item command="self"
                                  v-if="!tag.meta || !tag.meta.affix">关闭</el-dropdown-item>
              </el-dropdown-menu>
            </template>
          </el-dropdown>
        </router-link>
      </div>
    </scroll-panel>
  </div>
</template>

<script lang="ts">
import { defineComponent, computed, watch, onMounted, nextTick } from 'vue';
import { useRoute, RouteRecordRaw, useRouter } from 'vue-router';
import { useStore } from '@/store';
import { RouteLocationWithFullPath } from '@/store/modules/tagsView';
import path from 'path';
import { routes } from '@/router';
import ScrollPanel from '../ScrollPanel.vue';

// 右键菜单
enum TagCommandType {
  All = 'all',
  Other = 'other',
  Self = 'self',
  Refresh = 'refresh'
}
// 通过右键选择 关闭所有、 关闭其他、 关闭当前、刷新 对于tag affix为true的固定tag是不允许关闭删除的
export default defineComponent({
  name: 'TagsView',
  components: { ScrollPanel },
  setup() {
    const store = useStore();
    const route = useRoute();
    const router = useRouter();
    // 从store里获取 可显示的tags view
    const visitedTags = computed(() => store.state.tagsView.visitedViews);
    // 从路由表中过滤出要affixed tagviews
    const fillterAffixTags = (
      routes: Array<RouteLocationWithFullPath | RouteRecordRaw>,
      basePath = '/'
    ) => {
      let tags: RouteLocationWithFullPath[] = [];
      routes.forEach((route) => {
        if (route.meta && route.meta.affix) {
          // 把路由路径解析成完整路径，路由可能是相对路径
          const tagPath = path.resolve(basePath, route.path);
          tags.push({
            name: route.name,
            path: tagPath,
            fullPath: tagPath,
            meta: { ...route.meta }
          } as RouteLocationWithFullPath);
        }

        // 深度优先遍历 子路由（子路由路径可能相对于route.path父路由路径）
        if (route.children) {
          const childTags = fillterAffixTags(route.children, route.path);
          if (childTags.length) {
            tags = [...tags, ...childTags];
          }
        }
      });
      return tags;
    };

    // 初始添加affix的tag
    const initTags = () => {
      const affixTags = fillterAffixTags(routes);
      for (const tag of affixTags) {
        if (tag.name) {
          store.dispatch('tagsView/addVisitedView', tag);
        }
      }
    };
    // 添加tag
    const addTags = () => {
      const { name } = route;
      console.log('addTags', route);
      if (name) {
        store.dispatch('tagsView/addView', route);
      }
    };

    // 路径发生变化追加tags view
    watch(
      () => route.path,
      () => {
        addTags();
      }
    );

    // 最近当前router到tags view
    onMounted(() => {
      initTags();
      addTags();
    });

    // 是否是当前应该激活的tag
    const isActive = (tag: RouteRecordRaw) => {
      return tag.path === route.path;
    };
    // 让删除后tags view集合中最后一个为选中状态
    const toLastView = (
      visitedViews: RouteLocationWithFullPath[],
      view: RouteLocationWithFullPath
    ) => {
      // 得到集合中最后一个项tag view 可能没有
      const lastView = visitedViews[visitedViews.length - 1];
      if (lastView) {
        router.push(lastView.fullPath as string);
      } else {
        // 集合中都没有tag view时
        // 如果刚刚删除的正是Dashboard 就重定向回Dashboard（首页）
        if (view.name === 'Dashboard') {
          router.replace({ path: ('/redirect' + view.fullPath) as string });
        } else {
          // tag都没有了 删除的也不是Dashboard 只能跳转首页
          router.push('/');
        }
      }
    };
    // 关闭当前右键的tag路由
    const closeSelectedTag = (view: RouteLocationWithFullPath) => {
      store.dispatch('tagsView/delView', view).then(() => {
        // 如果移除的view是当前选中的view，删除后需要集中到最后一个tagsview
        if (isActive(view)) {
          toLastView(visitedTags.value, view);
        }
      });
    };
    // 是否是始终固定在tagsview上的tag
    const isAffix = (tag: RouteLocationWithFullPath) => {
      return tag.meta && tag.meta.affix;
    };
    // 右键菜单
    const handleTagCommand = (
      command: TagCommandType,
      view: RouteLocationWithFullPath
    ) => {
      switch (command) {
        case TagCommandType.All:
          handleCloseAllTag(view);
          break;
        case TagCommandType.Other: // 关闭其他tag 除了affix为true的和当前右键的tag
          handleCloseOtherTag(view);
          break;
        case TagCommandType.Self: // 关闭当前右键的tag affix为true的tag下拉菜单中无此项
          closeSelectedTag(view);
          break;
        case TagCommandType.Refresh: // 刷新当前右键选中tag对应的路由
          refreshSelectedTag(view);
          break;
        default:
          break;
      }
    };
    const handleCloseAllTag = (view: RouteLocationWithFullPath) => {
      store.dispatch('tagsView/delAllView').then(() => {
        // 关闭所有后，就让切换剩下affix中最后一个tag
        toLastView(visitedTags.value, view);
      });
    };
    // 删除其他tag 除了当前右键的tag
    const handleCloseOtherTag = (view: RouteLocationWithFullPath) => {
      store.dispatch('tagsView/delOthersViews', view).then(() => {
        if (!isActive(view)) {
          // 删除其他tag后 让该view路由激活
          router.push(view.path);
        }
      });
    };
    // 右键刷新 清空当前对应路由缓存
    const refreshSelectedTag = (view: RouteLocationWithFullPath) => {
      // 刷新前 将该路由名称从缓存列表中移除
      store.dispatch('tagsView/delCachedView', view).then(() => {
        const { fullPath } = view;
        nextTick(() => {
          router.replace('/redirect' + fullPath);
        });
      });
    };
    // 获取主题色
    const themeColor = computed(() => store.getters.themeColor);

    return {
      visitedTags,
      isActive,
      closeSelectedTag,
      isAffix,
      handleTagCommand,
      themeColor
    };
  }
});
</script>

<style lang="scss" scoped>
.tags-view-container {
  width: 100%;
  height: 34px;
  background: #fff;
  border-bottom: 1px solid #d8dce5;
  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
  overflow: hidden;
  .tags-view-wrapper {
    .tags-view-item {
      display: inline-block;
      height: 26px;
      line-height: 26px;
      border: 1px solid #d8dce5;
      background: #fff;
      color: #495060;
      padding: 0 8px;
      box-sizing: border-box;
      font-size: 12px;
      margin-left: 5px;
      margin-top: 4px;
      &:first-of-type {
        margin-left: 15px;
      }
      &:last-of-type {
        margin-right: 15px;
      }
      &.active {
        background-color: #42b983;
        color: #fff;
        border-color: #42b983;
        &::before {
          position: relative;
          display: inline-block;
          content: '';
          width: 8px;
          height: 8px;
          border-radius: 50%;
          margin-right: 5px;
          background: #fff;
        }
      }
    }
  }
}
</style>

<style lang="scss">
.tags-view-container {
  .el-icon-close {
    width: 16px;
    height: 16px;
    position: relative;
    left: 2px;
    border-radius: 50%;
    text-align: center;
    transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
    transform-origin: 100% 50%;
    &:before {
      transform: scale(0.6);
      display: inline-block;
      vertical-align: -1px;
    }
    &:hover {
      background-color: #b4bccc;
      color: #fff;
    }
  }
}
</style>
